
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>基于Jenkins和Kubernetes流水线实现应用的自动发版</title>
            </head>
            <body>
            <a href="https://andyoung.blog.csdn.net">原作者博客</a>
            <div id="content_views" class="markdown_views prism-atom-one-light">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <p>本文我们将基于Jenkins和Kubernetes流水线过程实现自动化构建Java应用，即把流水线的过程转换为pipeline语法，实现应用的自动发版。</p> 
<h2><a id="_Java_4"></a><strong>一、 创建Java测试用例</strong></h2> 
<p>本书为读者准备了一个简单的Java测试用例，用于读者的学习，可以从 https://github.com/andanyoung/springboot-hello.git 找到该项目（也可以使用公司的Java项目）。</p> 
<p>接下来将该项目导入自己的GitLab中。首先找到之前创建的Kubernetes组，然后单击New project，如图1所示。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/77a7d6b37bae2ffba67a643c842841c5.png" alt="图片"></p> 
<p>图1 创建测试项目</p> 
<p>选择Import project，如图2所示。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/19e41478cf9d227efe96d95218849c5e.png" alt="图片"></p> 
<p>图2 导入项目</p> 
<p>单击Repo by URL</p> 
<p>图3 导入Java测试项目</p> 
<p>在Git repository URL中输入示例地址，然后单击Create project，如图4所示。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/db514fb507e01bb79f53f9ff6407bd23.png" alt="图片"></p> 
<p>图4 创建Project</p> 
<p>导入后，结果如图5所示。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/10b778b4ab3d0179f7b9c6d36964f1c1.png" alt="图片"></p> 
<p>图5 查看导入的项目</p> 
<h2><a id="Jenkinsfile_36"></a><strong>二、定义Jenkinsfile</strong></h2> 
<p>Jenkinsfile定义流水线的步骤包括拉取代码、执行构建、生成镜像、更新Kubernetes资源等。本书将Jenkinsfile放置于项目源代码一并管理，也可以单独放置于一个Git仓库进行管理。</p> 
<p>接下来在GitLab的源代码中添加Jenkinsfile。首先单击代码首页的“+”，然后单击New file，如图6所示。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/22e3fc523144749762d793a43c0707b9.png" alt="图片"></p> 
<p>图6 添加文件</p> 
<p>在窗口中添加如下内容（完整内容见本书代码）：</p> 
<pre><code>pipeline {

 ...

 parameters {

  gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')

 }

}
</code></pre> 
<p>这个Jenkinsfile比较长，接下来我们分别解析一下。</p> 
<h2><a id="_Jenkinsfile_64"></a><strong>三、 Jenkinsfile详解</strong></h2> 
<p>首先是顶层的agent，定义Kubernetes的Pod作为Jenkins的Slave：</p> 
<pre><code>agent {

  # 定义使用Kubernetes作为agent

  kubernetes {

   选择的云为之前配置的名字

   cloud 'kubernetes-study'

   slaveConnectTimeout 1200

  #将workspace改成hostPath，因为该Slave会固定节点创建，如果有存储可用，可以改成PVC的模式

   workspaceVolume hostPathWorkspaceVolume(hostPath: "/opt/workspace", readOnly: false)

   yaml '''

apiVersion: v1

kind: Pod

spec:

 containers:

  # jnlp容器，和Jenkins主节点通信

  - args: ['$(JENKINS_SECRET)', '$(JENKINS_NAME)']

   image: 'registry.cn-beijing.aliyuncs.com/citools/jnlp:alpine'

   name: jnlp

   imagePullPolicy: IfNotPresent

   volumeMounts:

​    - mountPath: "/etc/localtime"

​     name: "localtime"

​     readOnly: false 

  # build容器，包含执行构建的命令，比如Java的需要mvn构建，就可以用一个maven的镜像 

  - command:

​    - "cat"

   env:

​    - name: "LANGUAGE"

​     value: "en_US:en"

​    - name: "LC_ALL"

​     value: "en_US.UTF-8"

​    - name: "LANG"

​     value: "en_US.UTF-8"

  # 使用Maven镜像，包含mvn工具。NodeJS可以用node的镜像

   image: "registry.cn-beijing.aliyuncs.com/citools/maven:3.5.3"

   imagePullPolicy: "IfNotPresent"

  # 容器的名字，流水线的stage可以直接使用该名字

   name: "build"

   tty: true

   volumeMounts:

​    - mountPath: "/etc/localtime"

​     name: "localtime"

​    # Pod单独创建了一个缓存的volume，将其挂载到了maven插件的缓存目录，默认是/root/.m2

​    - mountPath: "/root/.m2/"

​     name: "cachedir"

​     readOnly: false

  # 发版容器，因为最终是发版至Kubernetes的，所以需要有一个kubectl命令

  - command:

​    - "cat"

   env:

​    - name: "LANGUAGE"

​     value: "en_US:en"

​    - name: "LC_ALL"

​     value: "en_US.UTF-8"

​    - name: "LANG"

​     value: "en_US.UTF-8"

  # 镜像的版本可以替换为其他的版本，也可以不替换，因为只执行set命令，所以版本是兼容的

   image: "registry.cn-beijing.aliyuncs.com/citools/kubectl:self-1.17"

   imagePullPolicy: "IfNotPresent"

   name: "kubectl"

   tty: true

   volumeMounts:

​    - mountPath: "/etc/localtime"

​     name: "localtime"

​     readOnly: false

  # 用于生成镜像的容器，需要包含docker命令

  - command:

​    - "cat"

   env:

​    - name: "LANGUAGE"

​     value: "en_US:en"

​    - name: "LC_ALL"

​     value: "en_US.UTF-8"

​    - name: "LANG"

​     value: "en_US.UTF-8"

   image: "registry.cn-beijing.aliyuncs.com/citools/docker:20.10.9-git"

   imagePullPolicy: "IfNotPresent"

   name: "docker"

   tty: true

   volumeMounts:

​    - mountPath: "/etc/localtime"

​     name: "localtime"

​     readOnly: false

​    # 由于容器没有启动docker服务，因此将宿主机的docker直接挂载至容器即可

​    - mountPath: "/var/run/docker.sock"

​     name: "dockersock"

​     readOnly: false

 restartPolicy: "Never"

# 固定节点部署

 nodeSelector:

  build: "true"

 securityContext: {}

 volumes:

  # Docker socket volume

  - hostPath:

​    path: "/var/run/docker.sock"

   name: "dockersock"

  - hostPath:

​    path: "/usr/share/zoneinfo/Asia/Shanghai"

   name: "localtime"

  # 缓存目录

  - name: "cachedir"

   hostPath:

​    path: "/opt/m2"

'''

  }
</code></pre> 
<p>之后看一下Jenkinsfile最后的环境变量和parameters的配置：</p> 
<pre><code># 定义一些全局的环境变量

environment {

  COMMIT_ID = ""

  HARBOR_ADDRESS = "10.0.0.204" # Harbor地址

  REGISTRY_DIR = "kubernetes"  # Harbor的项目目录

  IMAGE_NAME = "spring-boot-project" # 镜像的名称

  NAMESPACE = "kubernetes" # 该应用在Kubernetes中的命名空间

  TAG = "" # 镜像的Tag，在此用BUILD_TAG+COMMIT_ID组成

 }

 parameters {

  # 之前讲过一些choice、input类型的参数，本次使用的是GitParameter插件

  # 该字段会在Jenkins页面生成一个选择分支的选项

  gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')

 }
</code></pre> 
<p>接下来是拉取代码的stage，这个stage是一个并行的stage，因为考虑了该流水线是手动触发还是自动触发：</p> 
<pre><code>stage('Pulling Code') {

   parallel {

​    stage('Pulling Code by Jenkins') {

​     when {

​      expression {

​      # 若env.gitlabBranch为空，则该流水线为手动触发，执行该stage；若不为空，则会执行同级的另一个stage

​       env.gitlabBranch == null

​      }

 

​     }

​     steps {

​      # 这里使用git插件拉取代码，BRANCH变量取自前面介绍的parameters配置

​      # git@xxxxxx:root/spring-boot-project.git代码地址

​      # credentialsId: 'gitlab-key'，之前创建的拉取代码的key

​      git(changelog: true, poll: true, url: 'git@xxxxxx:root/ spring-boot-project.git', branch: "${BRANCH}", credentialsId: 'gitlab-key')

​      script {

​      # 定义一些变量用于生成镜像的Tag

​      # 获取最近一次提交的Commit ID

​       COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()

​      # 赋值给TAG变量，后面的docker build可以取到该TAG的值

​       TAG = BUILD_TAG + '-' + COMMIT_ID

​       println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"

 

​      }

 

​     }

​    }

​    stage('Pulling Code by trigger') {

​     when {

​      expression {

​      # 如果env.gitlabBranch不为空，说明该流水线是通过webhook触发的，则此时执行该stage，上述的stage不再执行。此时BRANCH变量为空

​       env.gitlabBranch != null

​      }

 

​     }

​     steps {

​      # 以下配置和前面一致，只是此时branch: env.gitlabBranch取的值为env.gitlabBranch

​      git(url: 'git@xxxxxxxxxxx:root/spring-boot-project.git', branch: env.gitlabBranch, changelog: true, poll: true, credentialsId: 'gitlab-key')

​      script {

​       COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()

​       TAG = BUILD_TAG + '-' + COMMIT_ID

​       println "Current branch is ${env.gitlabBranch}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"

​      }

 

​     }

​    }

 

   }

  }
</code></pre> 
<p>代码拉下来后，就可以执行构建命令，由于本次实验是Java示例，因此需要使用mvn命令进行构建：</p> 
<pre><code>stage('Building') {

   steps {

​    # 使用Pod模板里面的build容器进行构建

​    container(name: 'build') {

​      sh """

​      # 编译命令，需要根据自己项目的实际情况进行修改，可能会不一致

​       mvn clean install -DskipTests

​      # 构建完成后，一般情况下会在target目录下生成jar包

​       ls target/*

​      """

​    }

   }

  }
</code></pre> 
<p>生成编译产物后，需要根据该产物生成对应的镜像，此时可以使用Pod模板的docker容器：</p> 
<pre><code>stage('Docker build for creating image') {

  # 首先取出HARBOR的账号和密码

environment {

​    HARBOR_USER   = credentials('HARBOR_ACCOUNT')

  }

   steps {

​    # 指定使用docker容器

​    container(name: 'docker') {

​     sh """

​     # 执行build命令，Dockerfile会在下一小节创建，也是放在代码仓库，和Jenkinsfile同级

​     docker build -t ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} .

​     # 登录Harbor，HARBOR_USER_USR和HARBOR_USER_PSW由上述environment生成

​     docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${HARBOR_ADDRESS}

​     # 将镜像推送至镜像仓库

​     docker push ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG}

​     """

​    }

   }

  }

最后一步就是将该镜像发版至Kubernetes集群中，此时使用的是包含kubectl命令的容器：

stage('Deploying to k8s') {

  # 获取连接Kubernetes集群证书 

environment {

​    MY_KUBECONFIG = credentials('study-k8s-kubeconfig')

  }

   steps {

​    # 指定使用kubectl容器

​    container(name: 'kubectl'){

​      sh """

​     # 直接set更改Deployment的镜像即可

​      /usr/local/bin/kubectl --kubeconfig $MY_KUBECONFIG set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n $NAMESPACE

​      """

​    }

   }

  }

 }
</code></pre> 
<blockquote> 
 <p><strong>注意</strong><br> 本例发版的命令为/usr/local/bin/kubectl --kubeconfig <span class="katex--inline"><span class="katex"><span class="katex-mathml"> 
      
       
        
        
          M 
         
         
         
           Y 
          
         
           K 
          
         
        
          U 
         
        
          B 
         
        
          E 
         
        
          C 
         
        
          O 
         
        
          N 
         
        
          F 
         
        
          I 
         
        
          G 
         
        
          s 
         
        
          e 
         
        
          t 
         
        
          i 
         
        
          m 
         
        
          a 
         
        
          g 
         
        
          e 
         
        
          d 
         
        
          e 
         
        
          p 
         
        
          l 
         
        
          o 
         
        
          y 
         
        
          − 
         
        
          l 
         
        
          a 
         
        
          p 
         
        
          p 
         
        
          = 
         
        
       
         MY_KUBECONFIG set image deploy -l app= 
        
       
     </span><span class="katex-html"><span class="base"><span class="strut" style="height: 0.88888em; vertical-align: -0.19444em;"></span><span class="mord mathdefault" style="margin-right: 0.10903em;">M</span><span class="mord"><span class="mord mathdefault" style="margin-right: 0.22222em;">Y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height: 0.328331em;"><span class="" style="top: -2.55em; margin-left: -0.22222em; margin-right: 0.05em;"><span class="pstrut" style="height: 2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right: 0.07153em;">K</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height: 0.15em;"><span class=""></span></span></span></span></span></span><span class="mord mathdefault" style="margin-right: 0.10903em;">U</span><span class="mord mathdefault" style="margin-right: 0.05017em;">B</span><span class="mord mathdefault" style="margin-right: 0.05764em;">E</span><span class="mord mathdefault" style="margin-right: 0.07153em;">C</span><span class="mord mathdefault" style="margin-right: 0.02778em;">O</span><span class="mord mathdefault" style="margin-right: 0.10903em;">N</span><span class="mord mathdefault" style="margin-right: 0.13889em;">F</span><span class="mord mathdefault" style="margin-right: 0.07847em;">I</span><span class="mord mathdefault">G</span><span class="mord mathdefault">s</span><span class="mord mathdefault">e</span><span class="mord mathdefault">t</span><span class="mord mathdefault">i</span><span class="mord mathdefault">m</span><span class="mord mathdefault">a</span><span class="mord mathdefault" style="margin-right: 0.03588em;">g</span><span class="mord mathdefault">e</span><span class="mord mathdefault">d</span><span class="mord mathdefault">e</span><span class="mord mathdefault">p</span><span class="mord mathdefault" style="margin-right: 0.01968em;">l</span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right: 0.03588em;">y</span><span class="mspace" style="margin-right: 0.222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right: 0.222222em;"></span></span><span class="base"><span class="strut" style="height: 0.88888em; vertical-align: -0.19444em;"></span><span class="mord mathdefault" style="margin-right: 0.01968em;">l</span><span class="mord mathdefault">a</span><span class="mord mathdefault">p</span><span class="mord mathdefault">p</span><span class="mspace" style="margin-right: 0.277778em;"></span><span class="mrel">=</span></span></span></span></span>{IMAGE_NAME} <span class="katex--inline"><span class="katex"><span class="katex-mathml"> 
      
       
        
         
         
           I 
          
         
           M 
          
         
           A 
          
         
           G 
          
          
          
            E 
           
          
            N 
           
          
         
           A 
          
         
           M 
          
         
           E 
          
         
        
          = 
         
        
       
         {IMAGE_NAME}= 
        
       
     </span><span class="katex-html"><span class="base"><span class="strut" style="height: 0.83333em; vertical-align: -0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right: 0.07847em;">I</span><span class="mord mathdefault" style="margin-right: 0.10903em;">M</span><span class="mord mathdefault">A</span><span class="mord mathdefault">G</span><span class="mord"><span class="mord mathdefault" style="margin-right: 0.05764em;">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height: 0.328331em;"><span class="" style="top: -2.55em; margin-left: -0.05764em; margin-right: 0.05em;"><span class="pstrut" style="height: 2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right: 0.10903em;">N</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height: 0.15em;"><span class=""></span></span></span></span></span></span><span class="mord mathdefault">A</span><span class="mord mathdefault" style="margin-right: 0.10903em;">M</span><span class="mord mathdefault" style="margin-right: 0.05764em;">E</span></span><span class="mspace" style="margin-right: 0.277778em;"></span><span class="mrel">=</span></span></span></span></span>{HARBOR_ADDRESS}/<span class="katex--inline"><span class="katex"><span class="katex-mathml"> 
      
       
        
         
         
           R 
          
         
           E 
          
         
           G 
          
         
           I 
          
         
           S 
          
         
           T 
          
         
           R 
          
          
          
            Y 
           
          
            D 
           
          
         
           I 
          
         
           R 
          
         
        
          / 
         
        
       
         {REGISTRY_DIR} / 
        
       
     </span><span class="katex-html"><span class="base"><span class="strut" style="height: 1em; vertical-align: -0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right: 0.00773em;">R</span><span class="mord mathdefault" style="margin-right: 0.05764em;">E</span><span class="mord mathdefault">G</span><span class="mord mathdefault" style="margin-right: 0.07847em;">I</span><span class="mord mathdefault" style="margin-right: 0.05764em;">S</span><span class="mord mathdefault" style="margin-right: 0.13889em;">T</span><span class="mord mathdefault" style="margin-right: 0.00773em;">R</span><span class="mord"><span class="mord mathdefault" style="margin-right: 0.22222em;">Y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height: 0.328331em;"><span class="" style="top: -2.55em; margin-left: -0.22222em; margin-right: 0.05em;"><span class="pstrut" style="height: 2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right: 0.02778em;">D</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height: 0.15em;"><span class=""></span></span></span></span></span></span><span class="mord mathdefault" style="margin-right: 0.07847em;">I</span><span class="mord mathdefault" style="margin-right: 0.00773em;">R</span></span><span class="mord">/</span></span></span></span></span>{IMAGE_NAME}😒{TAG} -n $NAMESPACE。由于本书示例的Deployment名称、Label、容器名称均和项目名称一致，因此就统一了IMAGE_NAME，如果读者不是按照此规范命名的，就需要进行更改，可以参考本书5.2节Deployment部分的kubectl set命令。</p> 
</blockquote> 
<h2><a id="_Dockerfile_523"></a><strong>四、 定义Dockerfile</strong></h2> 
<p>在执行流水线过程时，需要将代码的编译产物做成镜像。Dockerfile主要写的是如何生成公司业务的镜像，而本示例是Java项目，只需要把JAR包放在有JRE环境的镜像中，然后启动该JAR包即可：</p> 
<pre><code># 基础镜像可以按需修改，可以更改为公司自有的镜像

FROM registry.cn-beijing.aliyuncs.com/dotbalo/jre:8u211-data

# JAR包名称改成实际的名称，本示例为spring-cloud-eureka-0.0.1-SNAPSHOT.jar

COPY target/spring-cloud-eureka-0.0.1-SNAPSHOT.jar ./

# 启动JAR包

CMD java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar
</code></pre> 
<h2><a id="_Kubernetes_541"></a><strong>五、 定义Kubernetes资源</strong></h2> 
<p>本书的示例在GitLab创建的Group为kubernetes，可以认为其是一个项目，同一个项目可以部署至Kubernetes集群中的同一个Namespace中，本示例为kubernetes命名空间。由于使用的是私有仓库，因此需要先配置拉取私有仓库镜像的密钥：</p> 
<pre><code># kubectl create ns kubernetes

namespace/kubernetes created

# kubectl create secret docker-registry harborkey --docker-server=CHANGE_HERE_FOR_YOUR_HARBOR_ADDRESS --docker-username=admin --docker-password=Harbor12345 --docker-email=mail@kubeasy.com -n kubernetes

secret/harborkey created
</code></pre> 
<p>配置该应用的Deployment（部分内容），需要注意文件的文字注释部分：</p> 
<pre><code>apiVersion: apps/v1

kind: Deployment

metadata:

 creationTimestamp: null

 labels:

  app: spring-boot-project # Deployment标签，和流水线的set -l一致

 name: spring-boot-project # Deployment名称

 namespace: kubernetes

spec:

 replicas: 1

 selector:

  matchLabels:

   app: spring-boot-project # Pod的标签

 template:

  metadata:

   creationTimestamp: null

   labels:

    app: spring-boot-project # Pod的标签

  spec:

   ...

    - name: LANG

     value: C.UTF-8

    image: nginx # 此处使用的nginx作为原始的镜像，通过Jenkins构建并发版后，变成Java应用的镜像

    imagePullPolicy: IfNotPresent

    lifecycle: {}

    livenessProbe:

     failureThreshold: 2

     initialDelaySeconds: 30

     periodSeconds: 10

     successThreshold: 1

     tcpSocket:

      port: 8761 # 端口号和健康检查按照实际情况进行修改

     timeoutSeconds: 2

    name: spring-boot-project # 容器的名称，需要和流水线set命令的容器名称一致

    ports:

    - containerPort: 8761 # 端口号按照实际情况进行修改

     name: web

     protocol: TCP

    readinessProbe:

     failureThreshold: 2

     initialDelaySeconds: 30

     periodSeconds: 10

     successThreshold: 1

     tcpSocket:

      port: 8761 # 端口号和健康检查按照实际情况进行修改

     timeoutSeconds: 2

    resources: # 资源请求按照实际情况修改

     limits:

      cpu: 994m

      memory: 1170Mi

     requests:

      cpu: 10m

      memory: 55Mi

   dnsPolicy: ClusterFirst

   imagePullSecrets:

   - name: harborkey # Harbor仓库密钥，需要和上述创建的Secret一致

   restartPolicy: Always

   securityContext: {}

   serviceAccountName: default

定义该应用的Service和Ingress：

---

apiVersion: v1

kind: Service

metadata:

 creationTimestamp: null

 labels:

  app: spring-boot-project

 name: spring-boot-project

 namespace: kubernetes

spec:

 ports: # 端口按照实际情况进行修改

 - name: web

  port: 8761

  protocol: TCP

  targetPort: 8761

 selector:

  app: spring-boot-project

 sessionAffinity: None

 type: ClusterIP

status:

 loadBalancer: {}

---(部分内容)

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

 creationTimestamp: null

 name: spring-boot-project

 ...

    path: /

    pathType: ImplementationSpecific
</code></pre> 
<p>需要注意的是，在实际使用时，Java一般是后台应用，不需要使用Ingress暴露出去，只需要配置Service即可。如果需要被集群外部的用户直接访问，需要配置Ingress，通过域名发布。</p> 
<p>创建该资源：</p> 
<pre><code># kubectl create -f spring-boot-project.yaml

service/spring-boot-project created

ingress.networking.k8s.io/spring-boot-project created

deployment.apps/spring-boot-project created

</code></pre>
                </div>
            </body>
            </html>
            